﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Threading.Tasks;
using Castle.DynamicProxy.Generators.Emitters.SimpleAST;
using DailyDiet.Models;
using DailyDiet.Services.Constant;
using DailyDiet.Services.Navigation;
using DailyDiet.util;
using Xamarin.Forms.Internals;
using Expression = System.Linq.Expressions.Expression;

namespace DailyDiet.Services.Impl
{
    /// <summary>
    /// 基于某一规则生成一个菜品组合列表，然后传入RecommendService得到推荐系数
    /// </summary>
    public class GetRecService : IGetRecService {
        private IDietStorageService _dietStorageService;
        private IRecommendService _recommendService;

        private ICacheSerializeService _cacheSerializeService;

        private IPreferenceStorage _preference;

        
        public async Task<List<List<Diet>>> GetDietRecAsync(string mealTimes) {
            string timeKey;
            string fileName;
            string cacheKey;
            DietTemplate template = getRandomDietTemplate(mealTimes);
            if (mealTimes.Equals(GetRecServiceConstant.BREAKFAST)) {
                timeKey = GetRecServiceConstant.BreakfastTimekey;
                fileName = GetRecServiceConstant.BreakfastFileName;
                cacheKey = CacheSerializeConstant.RecBreakfastList;
            } else if(mealTimes.Equals(GetRecServiceConstant.DINNER)) {
                timeKey = GetRecServiceConstant.DinnerTimeKey;
                fileName = GetRecServiceConstant.DinnerFileName;
                cacheKey = CacheSerializeConstant.RecDinnerList;
            }
            else if (mealTimes.Equals(GetRecServiceConstant.LUNCH)) {
                timeKey = GetRecServiceConstant.LunchTimeKey;
                fileName = GetRecServiceConstant.LunchFileName;
                cacheKey = CacheSerializeConstant.RecLunchList;
            } else {
                throw new MealTimesNotExistException();
            }

            if (IsMakeRec(mealTimes)) {
                return (await _cacheSerializeService.DeSerializeAsync<List<List<Diet>>>(cacheKey)).CacheObject as List<List<Diet>>;
            }
            
            //进行查找
            Dictionary<int, List<Diet>> dietLists =
                new Dictionary<int, List<Diet>>();

            for (int i = 0; i < template.Num; i++)
            {
                var random = new Random(Guid.NewGuid().GetHashCode());
                
                string kind = template.kinds[i];
                Expression<Func<Diet, bool>> where = p =>
                    p.Kind.Equals(kind);

                var list =
                    await _dietStorageService.GetAllDietsAsync(where);
                dietLists[i] = new List<Diet>();

                for (int branch = 0; branch < list.Count&&branch<5; branch++)
                {
                    dietLists[i].Add(list[random.Next(0, list.Count-1)]);
                }
            }

            //生成score-list
            var scoreList = MakePermutationAndCombination(dietLists);

            //筛选最高分
            var scores = scoreList.Keys.ToList();
            scores.Sort();

            List<List<Diet>> result = new List<List<Diet>>();
            //不一定返回5条，加了一个条件了
            for (int i = 1; i < 6 && i <= scores.Count; i++)
            {
                result.Add(scoreList[scores[scores.Count - i]]);
            }
            //缓存
            Cache<List<List<Diet>>> cache = new Cache<List<List<Diet>>>() { CacheObject = result };
            await _cacheSerializeService.SerializeAsync(cache,cacheKey);
            //设置过期时间
            DateTime now = DateTime.Now;
            DateTime deadTime = Convert.ToDateTime(now.ToString("yyyy-MM-dd"))
                .AddDays(1);

            _preference.Set(timeKey,deadTime);
            return result;
        }

        /// <summary>
        /// 是否生成推荐菜品列表
        /// </summary>
        /// <param name="mealTimes">餐次</param>
        /// <returns></returns>
        public bool IsMakeRec(string mealTimes) {
            string timeKey;
            if (mealTimes.Equals(GetRecServiceConstant.BREAKFAST))
            {
                timeKey = GetRecServiceConstant.BreakfastTimekey;
            }
            else if (mealTimes.Equals(GetRecServiceConstant.DINNER))
            {
                timeKey = GetRecServiceConstant.DinnerTimeKey;
            }
            else if (mealTimes.Equals(GetRecServiceConstant.LUNCH))
            {
                timeKey = GetRecServiceConstant.LunchTimeKey;
            }
            else
            {
                throw new MealTimesNotExistException();
            }
            DateTime CacheDeadTime =
                _preference.Get(timeKey, GetRecServiceConstant.DefaultDateTime);
            if (CacheDeadTime < DateTime.Now) {
                return false;
            }

            return true;
        }

        private Dictionary<Double,List<Diet>> MakePermutationAndCombination(
            Dictionary<int, List<Diet>> dietLists) {
            var scoreAndList = new Dictionary<Double, List<Diet>>();
            if (dietLists.Keys.Count == 3) {
                foreach (var diet1 in dietLists[0]) {
                    foreach (var diet2 in dietLists[1]) {
                        foreach (var diet3 in dietLists[2]) {
                            var list = new List<Diet>() {diet1, diet2, diet3};
                            var scoreKey =
                                _recommendService.GetRecommendIndex(list);
                            if (scoreAndList.ContainsKey(scoreKey)) {
                                break;
                            }
                            scoreAndList.Add(scoreKey,list);
                        }
                    }
                }
            }
            return scoreAndList;
        }

        private DietTemplate getRandomDietTemplate(string mealTimes) {
            DietTemplate dietTemplate;
            if (mealTimes.Equals(GetRecServiceConstant.BREAKFAST)) {
                dietTemplate = DietTemplates.BreakfastTemplate;
            }
            else if (mealTimes.Equals(GetRecServiceConstant.DINNER)) {
                dietTemplate = DietTemplates.DinnerTemplate;
            }
            else if (mealTimes.Equals(GetRecServiceConstant.LUNCH)) {
                dietTemplate = DietTemplates.LunchTemplate;
            }
            else {
                dietTemplate = null;
                throw new MealTimesNotExistException();
            }

            return dietTemplate;
        }

        public GetRecService(
            IDietStorageService dietStorageService, 
            IRecommendService recommendService,
            IPreferenceStorage preferenceStorage,
            ICacheSerializeService cacheSerializeService) {
            
            _recommendService = recommendService;
            _dietStorageService = dietStorageService;
            _preference = preferenceStorage;
            _cacheSerializeService = cacheSerializeService;
        }
    }
}
